﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Linq;
using System.Text;
using System.Windows.Forms;

namespace JCChangeCalculatorControl
{
    delegate void numberPressedDelegate(string number);
    public delegate void amountDelegate(decimal totalAmount, decimal givenAmount, decimal changeAmount);

    public partial class ChangeCalcControl : UserControl
    {
        decimal _totalAmount = 0;
        decimal _givenAmount = 0;
        decimal _changeAmount = 0;
        decimal _mult = Convert.ToDecimal(.01);
        string _tempGivenString;
        private billFactory _billFactory;
        private List<bill> _bills = new List<bill>();
        private event numberPressedDelegate pressedNum;
        public event amountDelegate changeMade;

        public ChangeCalcControl()
        {
            InitializeComponent();
            resetUI();
            wireupButtons();
        }

       #region private Members

        void resetUI()
        {
            this.totalAmountLabel.Text = "$0.00";
            this.givenAmountLabel.Text = "$0.00";
            this.changeAmountLabel.Text = "$0.00";
            this.billButton1.Text = string.Empty;
            this.billButton2.Text = string.Empty;
            this.billButton3.Text = string.Empty;
            this.billButton4.Text = string.Empty;
            this.payButton.Enabled = false;
        }

        void wireupButtons()
        {
            // Each number button has a tag of n and an event handler will be
            // added for each number button. The numberButton_Click method raises
            // the pressedNum event and passes the buttons text value.
            foreach (Control c in this.mainPanel.Controls)
            { 
                if ((string)c.Tag == "n")
                    c.Click += new EventHandler(this.numberButton_Click);
            }

            // Each bill button has a tag of b and an event handler will be
            // added for each bill button. The billButton_Click method raises
            // the pressedNum event and passes the minipulated value of the buttons
            // text value.
            foreach (Control c in this.mainPanel.Controls)
            {
                if ((string)c.Tag == "b")
                    c.Click += new EventHandler(this.billButton_Click);            
            }

            // The pressedNum event is wired to the addNumber method.
            // It passes along the buttons text value wich is ultimately
            // used to concat the amount entered by the user.
            this.pressedNum += new numberPressedDelegate(this.addNumber);

            // wire the back button to the removeNumber method
            this.backButton.Click += new EventHandler(this.removeNumber);

            this.payButton.Click += new EventHandler(payButton_Click);

        }

        void payButton_Click(object sender, EventArgs e)
        {
            if (changeMade != null)
            changeMade(_totalAmount, _givenAmount, _changeAmount);
        }

        void checkPayButton()
        {
            if (_givenAmount >= _totalAmount)
            { this.payButton.Enabled = true; }
            else
            { this.payButton.Enabled = false; }
        }

        void numberButton_Click(object sender, EventArgs e)
        {
            // this is wired to all the number buttons and raises a private event.
            this.pressedNum(((Button)sender).Text);
        }

        void billButton_Click(object sender, EventArgs e)
        {
            // clear the temp string before calling the method.
            // This causes billButton presses to clear out any
            // previous entries.
            _tempGivenString = string.Empty;
            this.pressedNum(this.convertBillButtonAmount(((Button)sender).Text));

            // By clearing this variable after we process the amount
            // the user will be able to enter a new value using the
            // number buttons if needed. ie. If a person gives a 100
            // dollar bill but then changes their mind and gives $70
            // after the user presses the 100 bill button they can
            // just type in the $70.00 amount without having to back
            // out the $100.00 amount
            _tempGivenString = string.Empty;
        }

        string convertBillButtonAmount(string buttonText)
        { 
            // billbutton amounts are treated differenty than number buttons.
            // amount needs to be multiplied by 100 to be handled correctly
            // when the amount is shown using showGivenAmount.
            return Convert.ToString(Convert.ToDecimal(buttonText) * 100);
        }

        void addNumber(string number)
        {
            // This method recieves the number buttons text value through a series
            // of events. It concats the string, converts it to decimal, formats it
            // as money, and shows the result on the ui.
            _tempGivenString += number;

            this.showGivenAmount();
            this.tryChangeCalc();
            this.checkPayButton();
        }

        void removeNumber(object sender, EventArgs e)
        {
            // Pull off the last character from the string and display the new amount.
            if (_tempGivenString.Length > 0)
                _tempGivenString = _tempGivenString.Remove(_tempGivenString.Length - 1, 1);

            this.showGivenAmount();
            tryChangeCalc();
            this.checkPayButton();
        }

        void tryChangeCalc()
        {
            if (_givenAmount > _totalAmount)
            {
                calculateChangeAmount();
                showChangeAmount();
            }
            else
            {
                this.clearChangeAmount();
            }
        }

        void showGivenAmount()
        { 
             // _givenAmount is the decimal value that will be passed when the user
            // asks to calculate the change amount that is due.
            if (!string.IsNullOrEmpty(_tempGivenString))
            {
                _givenAmount = Convert.ToDecimal(_tempGivenString) * _mult;
            }
            else
            {
                _givenAmount = 0;
            }

            this.givenAmountLabel.Text = string.Format("{0:C}", _givenAmount);
        }

        void showTotalAmount()
        {
            this.totalAmountLabel.Text = string.Format("{0:C}", _totalAmount);
        }

        void showChangeAmount()
        {
            this.changeAmountLabel.Text = string.Format("{0:C}", _changeAmount);
        }

        void clearChangeAmount()
        {
            _changeAmount = 0;
            this.showChangeAmount();
        }

        void calculateChangeAmount()
        {
            this._changeAmount = this._givenAmount - this._totalAmount;
        }

        void BuildBillButtons()
        { 
            // Start with the totalAmount and build the four most
            // obvious bills that the customer could be paying with.
            // This method runs each time the totalAmount is set.
            this._billFactory = new billFactory();
            this._bills.Clear();
            this._bills = this._billFactory.MakeBills(4, this._totalAmount);
            int i = 0;

            // show/hide buttons
            foreach (Control c in this.mainPanel.Controls)
            {
                if ((string)c.Tag == "b")
                    if (_bills.ElementAt(i).billAmount.ToString() == "0")
                    { 
                        c.Text = string.Empty; 
                        c.Visible = false; 
                        i++;
                    }
                    else
                    { 
                        c.Text = _bills.ElementAt(i).billAmount.ToString();
                        c.Visible = true;
                        i++;
                    }
            }
 

        }

        #endregion


       #region Public Members

        public decimal totalAmount
        {
            get { return _totalAmount; }
            set {
                _totalAmount = value;
                _tempGivenString = string.Empty;
                // when a new totalAmount is set the UI must be reset before
                // the new total amount is displayed.
                this.resetUI();
                this.BuildBillButtons();
                this.showTotalAmount();
            }
        }

        public decimal getAmountGiven
        {
            get { return this._givenAmount; }
        }

        public bool CalcEnabled
        {
            // enables or disables the mainPanel control
            // which effectively keeps the user from using
            // the control.
            get { return this.mainPanel.Enabled; }
            set { this.mainPanel.Enabled = value; }
        }

        public string payButtonText
        {
            get { return this.payButton.Text; }
            set { this.payButton.Text = value; }
        }

        #endregion
    }
}
